package session

import (
	"fmt"
	"log"
	"strings"

	"test.com/common"
	"test.com/fusion"
	"test.com/protocol"

	"github.com/go-redis/redis"
)

func finishGameRPCResp(errCode int32, errMsg string) *fusion.NetPacket {
	return finishResp(protocol.SCT_RPC_INVOKE_RESP, errCode, errMsg)
}

func (obj *GameSession) handleRegister(pck *fusion.NetPacket) int {
	var gsName string
	if !pck.IsReadableEmpty() {
		pck.ReadString(&gsName)
	}
	gsID, err := InstGameServerMgr.RegisterGameServer(obj, gsName)
	if err != nil {
		obj.SendPacket(finishResp(
			protocol.SCT_REGISTER_RESP, common.GsErrorCodeRegisterFailed, err.Error()))
		return fusion.SessionHandleKill
	}
	resp := finishResp(protocol.SCT_REGISTER_RESP, common.GsErrorCodeNone, "")
	resp.Write(gsID)
	obj.SendPacket(resp)
	return fusion.SessionHandleSuccess
}

func (obj *GameSession) handleVerifyAccountValidity(pck *fusion.NetPacket, info *fusion.RPCReqMetaInfo) int {
	var acct uint32
	var token string
	pck.Read(&acct, &token)
	fusion.GoPool.Submit(func() {
		var resp *fusion.NetPacket
		defer func() {
			obj.RPCReplySimple(resp, info)
		}()
		log.Printf("GameSession `%s` handleVerifyAccountValidity(%d,%s) ...\n", obj, acct, token)
		acctRedisKey := fmt.Sprintf("account/token/%d", acct)
		val, err := common.RedisDB.Get(acctRedisKey).Result()
		if err == redis.Nil {
			resp = finishGameRPCResp(common.GErrorCodeAccountValidityError, "account token is expired.")
			log.Printf("GameSession `%s` handleVerifyAccountValidity(%d) failed, %s.\n", obj, acct, "redis.Nil")
			return
		}
		if err != nil {
			resp = finishGameRPCResp(common.GErrorCodeInternalError, "")
			log.Printf("GameSession `%s` handleVerifyAccountValidity(%d) failed, %s.\n", obj, acct, err.Error())
			return
		}
		if val != token {
			resp = finishGameRPCResp(common.GErrorCodeAccountValidityError, "account token is error.")
			log.Printf("GameSession `%s` handleVerifyAccountValidity(%d) failed, %s.\n", obj, acct, "token is mismatch")
			return
		}
		common.RedisDB.Del(acctRedisKey)
		resp = finishGameRPCResp(common.GErrorCodeNone, "")
		log.Printf("GameSession `%s` handleVerifyAccountValidity(%d) is ok.\n", obj, acct)
	})
	return fusion.SessionHandleSuccess
}

func (obj *GameSession) handleCharacterCreate(pck *fusion.NetPacket, info *fusion.RPCReqMetaInfo) int {
	var gsID, acct uint32
	var name string
	pck.Read(&gsID, &acct, &name)
	fusion.GoPool.Submit(func() {
		var resp *fusion.NetPacket
		defer func() {
			obj.RPCReplySimple(resp, info)
		}()
		log.Printf("GameSession `%s` handleCharacterCreate(%d,%d,%s) ...\n", obj, gsID, acct, name)
		var characterNum int
		err := common.GlobalDB.QueryRow("SELECT COUNT(*) FROM `t_account_characters` "+
			"WHERE `accountId`=? AND `serverId`=? AND `deleteTime`=0",
			acct, gsID).Scan(&characterNum)
		if err != nil {
			resp = finishGameRPCResp(common.GErrorCodeInternalError, "")
			log.Printf("GameSession `%s` handleCharacterCreate(%d) failed, %s.\n", obj, acct, err.Error())
			return
		}
		if characterNum >= 4 {
			resp = finishGameRPCResp(common.GErrorCodeTooManyPlayerError, "character number is too many.")
			log.Printf("GameSession `%s` handleCharacterCreate(%d) failed, %s.\n", obj, acct, "character too many")
			return
		}
		rst, err := common.GlobalDB.Exec(
			"INSERT INTO `t_account_characters` VALUES(?,?,NULL,?,1,UNIX_TIMESTAMP(),0)",
			acct, gsID, name)
		if err != nil {
			if strings.HasPrefix(err.Error(), "Duplicate entry") {
				resp = finishGameRPCResp(common.GErrorCodePlayerDuplicateNameError, "character name is already exist.")
			} else {
				resp = finishGameRPCResp(common.GErrorCodeInternalError, "")
			}
			log.Printf("GameSession `%s` handleCharacterCreate(%d) failed, %s.\n", obj, acct, err.Error())
			return
		}
		characterID, err := rst.LastInsertId()
		if err != nil {
			resp = finishGameRPCResp(common.GErrorCodeInternalError, "")
			log.Printf("GameSession `%s` handleCharacterCreate(%d) failed, %s.\n", obj, acct, err.Error())
			return
		}
		resp = finishGameRPCResp(common.GErrorCodeNone, "")
		resp.Write(gsID, characterID)
		log.Printf("GameSession `%s` handleCharacterCreate(%d) is ok.\n", obj, acct)
	})
	return fusion.SessionHandleSuccess
}

func (obj *GameSession) handleCharacterDelete(pck *fusion.NetPacket, info *fusion.RPCReqMetaInfo) int {
	var characterId uint32
	pck.Read(&characterId)
	fusion.GoPool.Submit(func() {
		var resp *fusion.NetPacket
		defer func() {
			obj.RPCReplySimple(resp, info)
		}()
		log.Printf("GameSession `%s` handleCharacterDelete(%d) ...\n", obj, characterId)
		_, err := common.GlobalDB.Exec("UPDATE `t_account_characters` SET "+
			"`characterName`=CONCAT_WS(',',`characterName`,`characterId`),"+
			"`deleteTime`=UNIX_TIMESTAMP() WHERE `characterId`=?", characterId)
		if err != nil {
			resp = finishGameRPCResp(common.GErrorCodeInternalError, "")
			log.Printf("GameSession `%s` handleCharacterDelete(%d) failed, %s.\n", obj, characterId, err.Error())
			return
		}
		resp = finishGameRPCResp(common.GErrorCodeNone, "")
		log.Printf("GameSession `%s` handleCharacterDelete(%d) is ok.\n", obj, characterId)
	})
	return fusion.SessionHandleSuccess
}

func (obj *GameSession) handleGuildCreate(pck *fusion.NetPacket, info *fusion.RPCReqMetaInfo) int {
	var logicGsId, characterId uint32
	var guildName string
	pck.Read(&logicGsId, characterId, guildName)
	fusion.GoPool.Submit(func() {
		var resp *fusion.NetPacket
		defer func() {
			obj.RPCReplySimple(resp, info)
		}()
		log.Printf("GameSession `%s` handleGuildCreate(%d,%d,%s) ...\n", obj, logicGsId, characterId, guildName)
		rst, err := common.GlobalDB.Exec("INSERT INTO `t_guild` VALUES(NULL,?,?,?,UNIX_TIMESTAMP())",
			guildName, logicGsId, characterId)
		if err != nil {
			if strings.HasPrefix(err.Error(), "Duplicate entry") {
				resp = finishGameRPCResp(common.GErrorCodeGuildDuplicateNameError, "guild name is already exist.")
			} else {
				resp = finishGameRPCResp(common.GErrorCodeInternalError, "")
			}
			log.Printf("GameSession `%s` handleGuildCreate(%d) failed, %s.\n", obj, characterId, err.Error())
			return
		}
		guildID, err := rst.LastInsertId()
		if err != nil {
			resp = finishGameRPCResp(common.GErrorCodeInternalError, "")
			log.Printf("GameSession `%s` handleGuildCreate(%d) failed, %s.\n", obj, characterId, err.Error())
			return
		}
		resp = finishGameRPCResp(common.GErrorCodeNone, "")
		resp.Write(guildID)
		log.Printf("GameSession `%s` handleGuildCreate(%d) is ok.\n", obj, characterId)
	})
	return fusion.SessionHandleSuccess
}

func (obj *GameSession) handleGuildDelete(pck *fusion.NetPacket, info *fusion.RPCReqMetaInfo) int {
	var guildID uint32
	pck.Read(&guildID)
	fusion.GoPool.Submit(func() {
		var resp *fusion.NetPacket
		defer func() {
			obj.RPCReplySimple(resp, info)
		}()
		log.Printf("GameSession `%s` handleGuildDelete(%d) ...\n", obj, guildID)
		_, err := common.GlobalDB.Exec("DELETE FROM `t_guild` WHERE `Id`=?", guildID)
		if err != nil {
			resp = finishGameRPCResp(common.GErrorCodeInternalError, "")
			log.Printf("GameSession `%s` handleGuildDelete(%d) failed, %s.\n", obj, guildID, err.Error())
			return
		}
		resp = finishGameRPCResp(common.GErrorCodeNone, "")
		log.Printf("GameSession `%s` handleGuildDelete(%d) is ok.\n", obj, guildID)
	})
	return fusion.SessionHandleSuccess
}
